package com.fit.utils.se;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

/**
 * @describe: 树工具
 * @author: hcl
 * @date: 2018/9/7 14:34
 */
public class TreeUtils {

    /**
     * @describe: 树组装工具
     * @author: hcl
     * @date: 2018/9/7 14:41
     * @param: [list:待组装的集合, pidFieldName:pid的字段名, subFieldName:sub的字段名, rootVal:root的值,valTypePid:true 表示rootVal是pid,false表示rootVal的值是id或id集合]
     * @return java.util.List<T>
     */
    public static <T> List<T> assemblyTree(List<T> list,String idFieldName,String pidFieldName,String subFieldName,Object rootVal,boolean valTypePid){
        list = Tools.removeNull(list);
        if(EmptyUtils.isNotEmpty(list)){
            List<ModeAttribute<T>> modeAttributes = TreeUtils.getModeAttributes(list,idFieldName,pidFieldName);
            //获取所有的root节点
            List<ModeAttribute<T>> rootNodes = null;
            if(valTypePid){
                rootNodes = TreeUtils.getRootNodeByPid(modeAttributes,rootVal);
            }else{
                rootNodes = TreeUtils.getRootNodeById(modeAttributes,rootVal);
            }
            List<T> rootList = new ArrayList<>(rootNodes.size());
            if(EmptyUtils.isNotEmpty(rootNodes)){
                for(ModeAttribute<T> rootNode : rootNodes){
                    TreeUtils.recursiveAssemblyTree(rootNode, modeAttributes);
                    rootList.add(rootNode.getNode());
                }
            }
            TreeUtils.setSub(modeAttributes,subFieldName);
            return rootList;
        }else{
            return list;
        }
    }

    /**
     * @describe: 树组装工具
     * @author: hcl
     * @date: 2018/9/7 14:41
     * @param: [list:待组装的集合, pidFieldName:pid的字段名, subFieldName:sub的字段名, rootPidVal:root pid 的值]
     * @return java.util.List<T>
     */
    public static <T> List<T> assemblyTree(List<T> list,String idFieldName,String pidFieldName,String subFieldName,Object rootPidVal){
        return assemblyTree(list,idFieldName,pidFieldName,subFieldName,rootPidVal,true);
    }


    /**
     * @describe: 获取root节点
     * @author: hcl  
     * @date: 2018/9/7 14:50
     * @param: [list, idFieldName, rootVal]  
     * @return java.util.List<T>  
     */
    private static <T> List<ModeAttribute<T>> getRootNodeByPid(List<ModeAttribute<T>> modeAttributes,Object rootVal){
        List<ModeAttribute<T>> rootNodes = new ArrayList<>();
        for(ModeAttribute<T> modeAttribute : modeAttributes){
            if(Tools.equals(modeAttribute.getPid(),rootVal)){
                rootNodes.add(modeAttribute);
            }
        }
        return rootNodes;
    }

    /**
     * @describe: 获取root节点
     * @author: hcl
     * @date: 2018/9/7 14:50
     * @param: [list, idFieldName, rootVal]
     * @return java.util.List<T>
     */
    private static <T> List<ModeAttribute<T>> getRootNodeById(List<ModeAttribute<T>> modeAttributes,Object rootVal){
        List<ModeAttribute<T>> rootNodes = new ArrayList<>();
        if(rootVal instanceof List){
            List<Object> rootVals = (List)rootVal;
            if(rootVals.size() > 0){
                Object[] rootValueObjes = rootVals.toArray(new Object[rootVals.size()]);
                for(ModeAttribute<T> modeAttribute : modeAttributes){
                    if(Tools.equals(modeAttribute.getId(),rootValueObjes)){
                        rootNodes.add(modeAttribute);
                    }
                }
            }
        }else{
            for(ModeAttribute<T> modeAttribute : modeAttributes){
                if(Tools.equals(modeAttribute.getId(),rootVal)){
                    rootNodes.add(modeAttribute);
                }
            }
        }
        return rootNodes;
    }


    /**
     * @describe: 递归组装
     * @author: hcl  
     * @date: 2018/9/7 15:23
     * @param: [obj, idFieldName, pidFieldName, subFieldName]  
     * @return void  
     */
    private static <T> void recursiveAssemblyTree(ModeAttribute<T> rootNode,List<ModeAttribute<T>> modeAttributes){
        for(ModeAttribute<T> modeAttribute : modeAttributes){
            if(Tools.equals(rootNode.getId(),modeAttribute.getPid())){
                rootNode.addSub(modeAttribute.getNode());
                recursiveAssemblyTree(modeAttribute,modeAttributes);
            }
        }
    }



    /**
     * @describe: 获取模型属性
     * @author: hcl  
     * @date: 2018/9/7 15:52
     * @param: [list, idFieldName, pidFieldName]  
     * @return java.util.List<com.fit.utils.se.ModeAttribute<T>>  
     */
    private static <T> List<ModeAttribute<T>> getModeAttributes(List<T> list,String idFieldName,String pidFieldName){
        List<ModeAttribute<T>> modeAttributes = new ArrayList<>(list.size());
        for(T node : list){
            ModeAttribute<T> modeAttribute = new ModeAttribute<T>();
            Object id = ReflectUtil.executeGet(node,idFieldName);
            Object pid = ReflectUtil.executeGet(node,pidFieldName);
            modeAttribute.setId(id);
            modeAttribute.setPid(pid);
            modeAttribute.setNode(node);
            modeAttributes.add(modeAttribute);
        }
        return modeAttributes;
    }


    /**
     * @describe: 设置sub
     * @author: hcl
     * @date: 2018/9/7 16:43
     * @param: [modeAttributes]
     * @return void
     */
    private static <T> void setSub(List<ModeAttribute<T>> modeAttributes,String subFieldName){
        for(ModeAttribute<T>  modeAttribute : modeAttributes){
            List<T> subs = modeAttribute.getSub();
            if(EmptyUtils.isNotEmpty(subs)){
                ReflectUtil.executeSet(modeAttribute.getNode(),subFieldName,subs);
            }
        }
    }

}

/**
 * @describe: mode 属性
 * @author: hcl  
 * @date: 2018/9/7 15:42
 */
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
class ModeAttribute<T>{

    /**
     * @describe: id
     */
    private Object id;

    /**
     * @describe: pid
     */
    private Object pid;

    /**
     * @describe: 子节点
     */
    private List<T> sub;


    /**
     * @describe: 节点对象
     */
    private T node;


    /**
     * @describe: 添加sub
     * @author: hcl  
     * @date: 2018/9/7 15:59
     * @param: [s]  
     * @return void  
     */
    public void addSub(T s){
        if(sub == null){
            sub = new ArrayList<>();
        }
        sub.add(s);
    }

}
